#!/bin/bash

WORKDIR=$(cd $(dirname $0); pwd)
LORAXRUNDIR=$WORKDIR/loraxrun
LORAXTMPDIR=$WORKDIR/loraxtmp
RESULTDIR=$WORKDIR/result
PRODUCT="OpenCloudOS Stream"
PRODUCTNAME="OpenCloudOS-Stream"
PRODUCTSHORT="OCS"
RELEASE=1
BUILDARCH="$(uname -m)"

CONFDIR=$WORKDIR/conf
COMPSXML_BASEOS=$CONFDIR/comps-BaseOS-$BUILDARCH.xml
COMPSXML_APPSTREAM=$CONFDIR/comps-AppStream-$BUILDARCH.xml
COMPSXML_MINIMAL=$CONFDIR/comps-minimal-$BUILDARCH.xml
TREEINFO_TEMPLATES_EVERYTHING=$CONFDIR/treeinfo-templates-everything
TREEINFO_TEMPLATES_MINIMAL=$CONFDIR/treeinfo-templates-minimal
BASEOSLIST=$CONFDIR/ocs-BaseOS-$BUILDARCH.list
MINIMALLIST=$CONFDIR/ocs-minimal-$BUILDARCH.list
KSFILE=$CONFDIR/opencloudos-stream-$BUILDARCH.ks

REPODIR=$WORKDIR/repo
REPOFILE=$WORKDIR/packages.repo

CACHEDIR=$WORKDIR/caches

ISOMNT=$WORKDIR/isomnt
ISOTMP=$WORKDIR/isotmp

RELEASEDIR=$WORKDIR/release
RELEASEDATE=$(date +'%Y%m%d')
LIVEMEDIADIR=$WORKDIR/livemedia
LOOPDEV=$(losetup -f)

VERSION=
REPOS=()
BOOTISO=
EVERYTHINGISO=
MINIMALISO=
LIVEMEDIAISO=
BOOTVOLID=
EVERYTHINGVOLID=
MINIMALVOLID=
LIVEMEDIAVOLID=

SHA256_EFIBOOTIMG=
SHA256_INSTALLIMG=
SHA256_INITRDIMG=
SHA256_VMLINUZ=
TIMESTAMP=

function remove_deprecated_packages()
{
    rm -f $REPODIR/kernel-*5.18.19*.rpm
    rm -f $REPODIR/kernel-*5.4.119*.rpm
    rm -f $REPODIR/protobuf*3.21.12*.rpm
}

function download_packages()
{
    local ret=0

    if [ -d $CACHEDIR ] && [ "$CACHEDIR" != "/" ]; then
        rm -rf $CACHEDIR
    fi
    mkdir -p $CACHEDIR

    if [ -d $REPODIR ] && [ "$REPODIR" != "/" ]; then
        rm -rf $REPODIR
    fi
    mkdir -p $REPODIR

    pushd $REPODIR

    # Download packages
    for repo in ${REPOS[*]}; do
        ret=0
        echo "[$(date +'%F %T')] Begin to download packages from $repo."
        cat <<EOF > $REPOFILE
[local]
name=local
baseurl=$repo
gpgcheck=0

EOF
        for i in `seq 1 6`; do
            reposync --repoid=local --newest-only -c $REPOFILE
            if [ $? -ne 0 ]; then
                ret=$?
                echo "[$(date +'%F %T')] Failed to download packages from $repo, retry $i"
                continue
            fi
            break
        done

        if [ $ret -ne 0 ]; then
            echo "[$(date +'%F %T')] Failed to download packages from $repo."
            popd
            exit 1
        fi

        find local -name "*.rpm" -type f | xargs -I {} mv -f {} .
        rm -rf local

    done

    remove_deprecated_packages

    createrepo .
    if [ $? -ne 0 ]; then
        echo "[$(date +'%F %T')] Failed to execute createrepo."
        popd
        exit 1
    fi

    popd

    cat <<EOF > $REPOFILE
[Packages]
name=Packages
baseurl=file://${REPODIR}
gpgcheck=0

EOF

    echo "[$(date +'%F %T')] Download packages successfully."
}

function make_netinst_iso()
{
    if [ -d $RESULTDIR ] && [ "$RESULTDIR" != "/" ]; then
        rm -rf $RESULTDIR
    fi
    
    if [ ! -d "$LORAXTMPDIR" ]; then
        mkdir -p $LORAXTMPDIR
    fi
    
    if [ ! -d "$LORAXRUNDIR" ]; then
        mkdir -p $LORAXRUNDIR
    fi

    if [ ! -d "$RELEASEDIR" ]; then
        mkdir -p $RELEASEDIR
    fi
    
    pushd $LORAXRUNDIR

    echo "[$(date +'%F %T')] Begin to run lorax."
    echo "[$(date +'%F %T')] Current directory: $(pwd)."
    
    lorax --tmp $LORAXTMPDIR --product="$PRODUCT" --version=$VERSION --release=$RELEASE --volid="$BOOTVOLID" \
        --buildarch=$BUILDARCH --nomacboot --rootfs-size 4 --isfinal -s file://$REPODIR $RESULTDIR
    
    if [ ! -f $RESULTDIR/images/boot.iso ]; then
        echo "[$(date +'%F %T')] Failed to generate netinst.iso."
        popd
        exit 1
    fi

    cp -arf $RESULTDIR/images/boot.iso $RELEASEDIR/$BOOTISO
    pushd $RELEASEDIR
    sha256sum $BOOTISO > $BOOTISO.sha256
    popd

    popd
}

function makeiso_aarch64()
{
    local isoname=$1
    local volid=$2

    if [ -z "$isoname" ]; then
        echo "[$(date +'%F %T')] Empty isoname"
        exit 1
    fi

    mkisofs -o $RELEASEDIR/$isoname \
        -eltorito-alt-boot \
        -e images/efiboot.img \
        -no-emul-boot \
        -R -J -V $volid \
        -graft-points \
        .discinfo=.discinfo \
        images/pxeboot=images/pxeboot \
        images/efiboot.img=images/efiboot.img \
        images/install.img=images/install.img \
        EFI/BOOT=EFI/BOOT .
}

function makeiso_loongarch64()
{
    local isoname=$1
    local volid=$2

    if [ -z "$isoname" ]; then
        echo "[$(date +'%F %T')] Empty isoname"
        exit 1
    fi

    mkisofs -o $RELEASEDIR/$isoname \
        -eltorito-alt-boot \
        -e images/efiboot.img \
        -no-emul-boot \
        -full-iso9660-filenames \
        -joliet-long \
        -R -J -V $volid \
        -no-bak \
        -graft-points \
        -full-iso9660-filenames \
        .discinfo=.discinfo \
        images/pxeboot=images/pxeboot \
        images/efiboot.img=images/efiboot.img \
        images/install.img=images/install.img \
        EFI/BOOT=EFI/BOOT .
}

function makeiso_x86_64()
{
    local isoname=$1
    local volid=$2

    if [ -z "$isoname" ]; then
        echo "[$(date +'%F %T')] Empty isoname"
        exit 1
    fi

    mkisofs -o $RELEASEDIR/$isoname \
        -R -J -V $volid \
        --grub2-mbr boot/grub2/i386-pc/boot_hybrid.img \
        -partition_offset 16 \
        -appended_part_as_gpt \
        -append_partition 2 C12A7328-F81F-11D2-BA4B-00A0C93EC93B images/efiboot.img \
        -iso_mbr_part_type EBD0A0A2-B9E5-4433-87C0-68B6B72699C7 \
        -c boot.cat --boot-catalog-hide \
        -b images/eltorito.img \
        -no-emul-boot -boot-load-size 4 -boot-info-table --grub2-boot-info \
        -eltorito-alt-boot \
        -e '--interval:appended_partition_2:all::' -no-emul-boot \
        -graft-points \
        .discinfo=.discinfo \
        images/install.img=images/install.img \
        images/pxeboot=images/pxeboot \
        boot/grub2=boot/grub2 \
        boot/grub2/i386-pc=boot/grub2/i386-pc \
        images/eltorito.img=images/eltorito.img \
        EFI/BOOT=EFI/BOOT .
}

function generate_treeinfo_common()
{
    if [ -d $ISOMNT ] && [ "$ISOMNT" != "/" ]; then
        mountpoint -q $ISOMNT
        if [ $? -eq 0 ]; then
            umount $ISOMNT
        fi
        rm -rf $ISOMNT
    fi
    mkdir -p $ISOMNT

    if [ -d $ISOTMP ] && [ "$ISOTMP" != "/" ]; then
        rm -rf $ISOTMP
    fi

    # Copy files from netinst.iso
    mount $RELEASEDIR/$BOOTISO $ISOMNT
    cp -pRf $ISOMNT $ISOTMP
    umount $ISOMNT

    pushd $ISOTMP

    chmod 755 EFI/BOOT/*.EFI EFI/BOOT/*.efi images/pxeboot/vmlinuz
    chmod 644 EFI/BOOT/fonts/* images/*.img images/pxeboot/*.img

    if [ ! -f images/efiboot.img ]; then
        cp -ar $RESULTDIR/images/efiboot.img images
    fi

    SHA256_EFIBOOTIMG=$(sha256sum images/efiboot.img | awk '{print $1}')
    SHA256_INSTALLIMG=$(sha256sum images/install.img | awk '{print $1}')
    SHA256_INITRDIMG=$(sha256sum images/pxeboot/initrd.img | awk '{print $1}')
    SHA256_VMLINUZ=$(sha256sum images/pxeboot/vmlinuz | awk '{print $1}')

    popd
}

function generate_treeinfo()
{
    local isotype=$1

    TIMESTAMP=$(date +"%s")

    losetup $LOOPDEV images/efiboot.img
    kpartx -u $LOOPDEV
    mount $LOOPDEV $ISOMNT

    case $isotype in
        "everything")
            sed -i "s|$BOOTVOLID|$EVERYTHINGVOLID|g" $ISOMNT/EFI/BOOT/grub.cfg
            umount $ISOMNT
            kpartx -d $LOOPDEV
            losetup -d $LOOPDEV

            SHA256_EFIBOOTIMG=$(sha256sum images/efiboot.img | awk '{print $1}')
            cp $TREEINFO_TEMPLATES_EVERYTHING .treeinfo
            ;;
        "minimal")
            sed -i "s|$EVERYTHINGVOLID|$MINIMALVOLID|g" $ISOMNT/EFI/BOOT/grub.cfg
            umount $ISOMNT
            kpartx -d $LOOPDEV
            losetup -d $LOOPDEV

            SHA256_EFIBOOTIMG=$(sha256sum images/efiboot.img | awk '{print $1}')
            cp $TREEINFO_TEMPLATES_MINIMAL .treeinfo
            ;;
        *)
            echo "[$(date +'%F %T')] $1 unkown isotype [$isotype]."
            ;;
    esac


    sed -i \
        -e "s#_SHA256EFIBOOTIMG#${SHA256_EFIBOOTIMG}#g" \
        -e "s#_SHA256INSTALLIMG#${SHA256_INSTALLIMG}#g" \
        -e "s#_SHA256INITRDIMG#${SHA256_INITRDIMG}#g" \
        -e "s#_SHA256VMLINUZ#${SHA256_VMLINUZ}#g" \
        -e "s#_BUILDARCH#$BUILDARCH#g" \
        -e "s#_VERSION#$VERSION#g" \
        -e "s#_PLATFORMS#$BUILDARCH#g" \
        -e "s#_TIMESTAMP#${TIMESTAMP}#g" \
        .treeinfo
}

function make_everything_iso()
{
    pushd $ISOTMP
    # Generate .treeinfo
    generate_treeinfo "everything"

    # Download packages from local repo
    reposync --repoid=Packages --newest-only -c $REPOFILE
    if [ $? -ne 0 ]; then
        echo "[$(date +'%F %T')] Failed to execute reposync for make everything iso."
        popd # pushd $ISOTMP
        exit 1
    fi

    if [ -d $REPODIR ] && [ "$REPODIR" != "/" ]; then
        rm -rf $REPODIR
    fi

    pushd Packages
    createrepo .
    if [ $? -ne 0 ]; then
        echo "[$(date +'%F %T')] Failed to execute createrepo for make everything iso."
        popd # pushd Packages
        popd # pushd $ISOTMP
        exit 1
    fi
    popd # pushd Packages

    cat <<EOF > $REPOFILE
[Packages]
name=Packages
baseurl=file://$(pwd)/Packages
gpgcheck=0

EOF

    mkdir -p $ISOTMP/BaseOS/Packages $ISOTMP/AppStream/
    pushd $ISOTMP/BaseOS/Packages
    dnf download -y --resolve -c $REPOFILE --repo Packages --installroot $CACHEDIR --destdir `pwd` `cat $BASEOSLIST | tr '\n' ' '`
    if [ $? -ne 0 ]; then
        echo "[$(date +'%F %T')] Failed to download packages for BaseOS repo"
        popd # pushd $ISOTMP/BaseOS/Packages
        popd # pushd $ISOTMP
        exit 1
    fi
    popd # pushd $ISOTMP/BaseOS/Packages

    rm -rf $ISOTMP/Packages/repodata
    for pkg in `ls $ISOTMP/BaseOS/Packages`; do
        rm -f $ISOTMP/Packages/$pkg
    done
    mv $ISOTMP/Packages $ISOTMP/AppStream

    createrepo -g $COMPSXML_BASEOS $ISOTMP/BaseOS
    if [ $? -ne 0 ]; then
        echo "[$(date +'%F %T')] Failed to createrepo for BaseOS"
        exit 1
    fi

    createrepo -g $COMPSXML_APPSTREAM $ISOTMP/AppStream
    if [ $? -ne 0 ]; then
        echo "[$(date +'%F %T')] Failed to createrepo for AppStream"
        exit 1
    fi

    # Make everything iso
    case $BUILDARCH in
        "aarch64")
            sed -i "s|$BOOTVOLID|$EVERYTHINGVOLID|g" EFI/BOOT/grub.cfg
            makeiso_aarch64 "$EVERYTHINGISO" "$EVERYTHINGVOLID"
            sed -i "s|$EVERYTHINGVOLID|ISOLABEL|g" EFI/BOOT/grub.cfg
            break
            ;;
        "x86_64")
            sed -i "s|$BOOTVOLID|$EVERYTHINGVOLID|g" EFI/BOOT/grub.cfg
            sed -i "s|$BOOTVOLID|$EVERYTHINGVOLID|g" boot/grub2/grub.cfg
            makeiso_x86_64 "$EVERYTHINGISO" "$EVERYTHINGVOLID"
            sed -i "s|$EVERYTHINGVOLID|ISOLABEL|g" EFI/BOOT/grub.cfg
            sed -i "s|$EVERYTHINGVOLID|ISOLABEL|g" boot/grub2/grub.cfg
            break
            ;;
        "loongarch64")
            sed -i "s|$BOOTVOLID|$EVERYTHINGVOLID|g" EFI/BOOT/grub.cfg
            makeiso_loongarch64 "$EVERYTHINGISO" "$EVERYTHINGVOLID"
            sed -i "s|$EVERYTHINGVOLID|ISOLABEL|g" EFI/BOOT/grub.cfg
            break
            ;;
        *)
            echo "[$(date +'%F %T')] Architecture $BUILDARCH not supported"
            ;;
    esac

    if [ ! -f $RELEASEDIR/$EVERYTHINGISO ]; then
        echo "[$(date +'%F %T')] Failed to generate everything.iso"
        popd # pushd $ISOTMP
        exit 1
    fi

    # Add md5 checksum to everything iso
    implantisomd5 $RELEASEDIR/$EVERYTHINGISO
    pushd $RELEASEDIR
    sha256sum $EVERYTHINGISO > $EVERYTHINGISO.sha256
    popd # $RELEASEDIR

    echo "[$(date +'%F %T')] everything.iso generated successfully!"

    popd # pushd $ISOTMP
    
}

function make_minimal_iso()
{
    cat <<EOF > $REPOFILE
[LocalBaseOS]
name=LocalBaseOS
baseurl=file://$ISOTMP/BaseOS
gpgcheck=0

[LocalAppStream]
name=LocalAppStream
baseurl=file://$ISOTMP/AppStream
gpgcheck=0

EOF

    pushd $ISOTMP
    generate_treeinfo "minimal"

    if [ -d Packages ]; then
        rm -rf Packages
    fi

    mkdir Packages
    pushd Packages
    dnf download -y --resolve -c $REPOFILE --repo LocalBaseOS,LocalAppStream --installroot $CACHEDIR --destdir `pwd` `cat $MINIMALLIST | tr '\n' ' '`
    if [ $? -ne 0 ]; then
        echo "[$(date +'%F %T')] Failed to download packages for minimal.iso"
        exit 1
    fi
    popd # Packages

    rm -rf AppStream BaseOS

    createrepo -g $COMPSXML_MINIMAL $ISOTMP
    if [ $? -ne 0 ]; then
        echo "[$(date +'%F %T')] Failed to createrepo for minimal Packages"
        exit 1
    fi

    # Make minimal iso
    case $BUILDARCH in
        "aarch64")
            sed -i "s|ISOLABEL|$MINIMALVOLID|g" EFI/BOOT/grub.cfg
            makeiso_aarch64 "$MINIMALISO" "$MINIMALVOLID"
            sed -i "s|$MINIMALVOLID|ISOLABEL|g" EFI/BOOT/grub.cfg
            break 
            ;;
        "x86_64")
            sed -i "s|ISOLABEL|$MINIMALVOLID|g" EFI/BOOT/grub.cfg
            sed -i "s|ISOLABEL|$MINIMALVOLID|g" boot/grub2/grub.cfg
            makeiso_x86_64 "$MINIMALISO" "$MINIMALVOLID"
            sed -i "s|$MINIMALVOLID|ISOLABEL|g" EFI/BOOT/grub.cfg
            sed -i "s|$MINIMALVOLID|ISOLABEL|g" boot/grub2/grub.cfg
            break
            ;;
        "loongarch64")
            sed -i "s|ISOLABEL|$MINIMALVOLID|g" EFI/BOOT/grub.cfg
            makeiso_loongarch64 "$MINIMALISO" "$MINIMALVOLID"
            sed -i "s|$MINIMALVOLID|ISOLABEL|g" EFI/BOOT/grub.cfg
            break
            ;;
        *)
            echo "[$(date +'%F %T')] Architecture $BUILDARCH not supported"
            ;;
    esac

    if [ ! -f $RELEASEDIR/$MINIMALISO ]; then
        echo "[$(date +'%F %T')] Failed to generate minimal.iso"
        popd # Packages
        exit 1
    fi

    # Add md5 checksum to everything iso
    implantisomd5 $RELEASEDIR/$MINIMALISO
    pushd $RELEASEDIR
    sha256sum $MINIMALISO > $MINIMALISO.sha256
    popd # $RELEASEDIR

    echo "[$(date +'%F %T')] minimal.iso generated successfully!"

    popd # Packages
}

function make_livemedia()
{
    if [ ! -d $LIVEMEDIADIR ] ; then
        mkdir -p $LIVEMEDIADIR
    fi

    pushd $LIVEMEDIADIR

    # create ksfile and add repos
    [ -f livemedia.ks ] && rm -f livemedia.ks
    cp -arf $KSFILE livemedia.ks
    i=0
    for repo in ${REPOS[*]}; do
        if [ "$i" == "0" ]; then
            echo "url --url=$repo" >> livemedia.ks
        fi
        echo "repo --name=\"repo-$i\" --baseurl=$repo" >> livemedia.ks
        i=$(($i+1))
    done

    [ -d result ] && rm -rf result

    /sbin/livemedia-creator \
	    --ks livemedia.ks \
	    --logfile livemedia-out.log \
	    --no-virt --make-iso \
	    --iso-only --nomacboot \
	    --resultdir result \
	    --project "$PRODUCT" \
	    --volid "$LIVEMEDIAVOLID" \
	    --iso-name $LIVEMEDIAISO \
	    --releasever $VERSION

    if [ $? -ne 0 ]; then
        echo "[$(date +'%F %T')] Failed to make livemedia"
	popd # $LIVEMEDIADIR
        exit 1
    fi

    mv -f result/$LIVEMEDIAISO $RELEASEDIR
    pushd $RELEASEDIR
    sha256sum $LIVEMEDIAISO > $LIVEMEDIAISO.sha256
    popd

    popd # $LIVEMEDIADIR

    echo "[$(date +'%F %T')] livemedia generated successfully!"
}

function usage()
{
    cat << EOF
Usage: sh $(basename $0) -v VERSION -s REPOSITORY -o RELEASEDIR 

    -v VERSION      OS release version.
    -s REPOSITORY   Source repository (may be listed multiple times).
    -o RELEASEDIR   Release directory. 
                    Default directory: $WORKDIR/release.
    -d RELEASEDATE  Release date (e.g., 20230301).

EOF
}

function parse_cmdline()
{
    ARGS=$(getopt -o v:s:o:d:h --long help -n "$0" -- "$@")

    if (( $? != 0 )); then
        usage
        exit 1
    fi

    eval set -- "$ARGS"
    while :; do
        case $1 in
            -v)
                VERSION=$2
                shift 2
                ;;
            -s)
                REPOS[${#REPOS[*]}]=$2
                shift 2
                ;;
            -o)
                RELEASEDIR=$2
                shift 2
                ;;
            -d)
                RELEASEDATE=$2
                shift 2
                ;;
            -h|--help)
                usage
                exit 0
                ;;
            --)
                shift
                break
                ;;
            *)
                echo "Unknown option: $1"
                usage
                exit 1
        esac
    done

    if [ -z "$VERSION" ]; then
        echo "[$(date +'%F %T')] Please give the release version."
        usage
        exit 1
    fi

    if [ "${RELEASEDIR:0:1}" != '/' ]; then
        RELEASEDIR=$WORKDIR/$RELEASEDIR
    fi

    if [ ${#REPOS[*]} == 0 ]; then
        echo "[$(date +'%F %T')] Please give a repository at least."
        usage
        exit 1
    fi

    BOOTISO=$PRODUCTNAME-$VERSION-$RELEASEDATE-netinst-$BUILDARCH.iso
    EVERYTHINGISO=$PRODUCTNAME-$VERSION-$RELEASEDATE-everything-$BUILDARCH.iso
    MINIMALISO=$PRODUCTNAME-$VERSION-$RELEASEDATE-minimal-$BUILDARCH.iso
    LIVEMEDIAISO=$PRODUCTNAME-$VERSION-$RELEASEDATE-live-$BUILDARCH.iso

    BOOTVOLID=$PRODUCTSHORT-$RELEASEDATE-Netinst
    EVERYTHINGVOLID=$PRODUCTSHORT-$RELEASEDATE-Everything
    MINIMALVOLID=$PRODUCTSHORT-$RELEASEDATE-Minimal
    LIVEMEDIAVOLID=$PRODUCTSHORT-$RELEASEDATE-Live
}

function check_command()
{
    which $1 1>/dev/null 2>&1
    if [ $? -ne 0 ]; then
        echo "[$(date +'%F %T')] $1 not found."
        return 1
    fi

    return 0
}

function check_commands()
{
    check_command reposync || exit 1
    check_command createrepo || exit 1
    check_command implantisomd5 || exit 1
    check_command lorax || exit 1
    check_command sha256sum || exit 1
    check_command mkisofs || exit 1
    check_command livemedia-creator || exit 1
}

function check_configure_file()
{
    if [ ! -f $1 ]; then
        echo "[$(date +'%F %T')] $1 not exist."
	return 1
    fi

    return 0
}

function check_configure_files()
{
    check_configure_file $COMPSXML_BASEOS || exit 1
    check_configure_file $COMPSXML_APPSTREAM || exit 1
    check_configure_file $COMPSXML_MINIMAL || exit 1
    check_configure_file $TREEINFO_TEMPLATES_EVERYTHING || exit 1
    check_configure_file $TREEINFO_TEMPLATES_MINIMAL || exit 1
    check_configure_file $BASEOSLIST || exit 1
    check_configure_file $MINIMALLIST || exit 1
    check_configure_file $KSFILE || exit 1
}

function check_rpm()
{
    rpm -q $1
    if [ $? -ne 0 ]; then
        echo "[$(date +'%F %T')] Package $1 should be installed."
        return 1
    fi

    return 0
}

function check_rpms()
{
    check_rpm lorax || exit 1
    check_rpm lorax-lmc-novirt || exit 1
    check_rpm fcoe-utils || exit 1
}

function main()
{
    parse_cmdline $@
    check_commands
    check_configure_files
    check_rpms
    download_packages
    make_netinst_iso
    generate_treeinfo_common
    make_everything_iso
    make_minimal_iso
    make_livemedia
}

main $@

exit 0
